// Copyright (c) 2020-present, INSPUR Co, Ltd. All rights reserved.
// This source code is licensed under Apache 2.0 License.
//
// Created by florian on 05.08.15.
//
// this class is used by ART module to store entire key which is parsed from
// leaf node.
//
#pragma once

#include <assert.h>
#include <cstring>
#include <iostream>
#include <memory>
#include <stdint.h>

namespace art_rowex {

using KeyLen = uint32_t;

class Key {
  static constexpr uint32_t stackLen = 128;
  uint32_t len = 0;

  uint8_t *data = nullptr;

  uint8_t stackKey[stackLen] = {0};

 public:
  Key() {}

  ~Key();

  Key(const Key &key) = delete;

  Key(Key &&key);

  void set(const char bytes[], const std::size_t length);

  void setWithoutCopy(const char bytes[], const std::size_t length);

  void dump() const;

  void operator=(const char key[]);

  bool operator==(const Key &k) const {
    if (k.getKeyLen() != getKeyLen()) {
      return false;
    }
    if (k.getKeyLen() == 0)  // if key is empty or null, we cannot throw error.
      return true;
    return std::memcmp(&k[0], data, getKeyLen()) == 0;
  }

  uint8_t &operator[](std::size_t i);

  const uint8_t &operator[](std::size_t i) const;

  KeyLen getKeyLen() const;

  void setKeyLen(KeyLen newLen);
};

inline uint8_t &Key::operator[](std::size_t i) {
  assert(i < len);
  return data[i];
}

inline const uint8_t &Key::operator[](std::size_t i) const {
  assert(i < len);
  return data[i];
}

inline KeyLen Key::getKeyLen() const { return len; }

inline Key::~Key() {
  if (len > stackLen) {
    delete[] data;
    data = nullptr;
  }
}

inline Key::Key(Key &&key) {
  len = key.len;
  if (len > stackLen) {
    data = key.data;
    key.data = nullptr;
  } else {
    memcpy(stackKey, key.stackKey, key.len);
    data = stackKey;
  }
}

inline void Key::set(const char bytes[], const std::size_t length) {
  if (len > stackLen) {
    delete[] data;
  }
  if (length <= stackLen) {
    memcpy(stackKey, bytes, length);
    data = stackKey;
  } else {
    data = new uint8_t[length];
    memcpy(data, bytes, length);
  }
  len = length;
}

inline void Key::setWithoutCopy(const char *uk, const std::size_t length) {
  data = (uint8_t *)uk;
  len = length;
}

inline void Key::dump() const {
  std::cout << "key len:" << len << ", key: ";
  for (uint32_t i = 0; i < len; i++) {
    std::cout << (char)data[i];
  }
  std::cout << std::endl;
}

inline void Key::operator=(const char key[]) {
  if (len > stackLen) {
    delete[] data;
  }
  len = strlen(key);
  if (len <= stackLen) {
    memcpy(stackKey, key, len);
    data = stackKey;
  } else {
    data = new uint8_t[len];
    memcpy(data, key, len);
  }
}

inline void Key::setKeyLen(KeyLen newLen) {
  if (len == newLen) return;
  if (len > stackLen) {
    delete[] data;
  }
  len = newLen;
  if (len > stackLen) {
    data = new uint8_t[len];
  } else {
    data = stackKey;
  }
}
}  // namespace art_rowex